package AnalisisLexico;

import utils.AbrirTxt;
import gui.MensajeError;

import AnalisisLexico.Token.PalabrasReservadasFunc;
import AnalisisLexico.Token.PalabrasReservadasTipos;
import AnalisisLexico.Token.Token;

public class AnalizadorLexico_v2 implements IAnalizadorLexico{

	   private String codigo;
	   private int numLinea = 1;
	   private PalabrasReservadasFunc palFunc;
	   private int indice = 0;
	   private MensajeError errores;
	   private PalabrasReservadasTipos palResvTipo = new PalabrasReservadasTipos();
	   
	   /**
	    * El constructor se encarga de recibir el nombre/ruta del fichero con el codigo
	    * fuente, y lo carga gracias a un clase AbrirTxt(creada por mi) al  atributo
	    * codigo, es decir, que mete en un atributo todos los caracteres del fichero.
	    * @param n
	    */
	   public AnalizadorLexico_v2(String n){
		   if (n.charAt(0) == '*'){
			   codigo = n.substring(1);
		   } else {
			   AbrirTxt open = new AbrirTxt(n);
			   open.cargaFichero();
			   codigo = open.getcontenido();
		   }
		   palFunc = new PalabrasReservadasFunc();
		   errores = new MensajeError();
	   }
	   
	   public MensajeError getErrores(){
		   return errores;
	   }
	   /**
	    * Este es el metodo clave, los tokens "unicaracteres" y el resto se reconocen aqui.
	    * Un resumen es el siguiente:
	    *    ->Tokens unicaracteres:
	    *      -' '
	    *      -'\n'
	    *      -'\t'
	    *      -'\r'
	    *      -'('
	    *      -')'
	    *      -'['
	    *      -']'
	    *      -'+'
	    *      -'*'
	    *      -'/'
	    *      -'%'
	    *      -';'
	    *      -'='
	    *      -'<'
	    *      -'>'
	    *      -'@'
	    *     ->Tokens bicaracteres:
	    *      -"=="
	    *      -">="
	    *      -"<="
	    *      -"&&"
	    *      -"||"
	    *     ->Tokens "serios":
	    *      -Numeros: Enteros y Reales
	    *      -Identificadores
	    *      
	    * @return vector con los tokens
	 * @throws Exception 
	 * @throws Exception 
	    */
	   public Token analiza() throws Exception {
		   boolean finToken = false;
		 //Quiero saber el numero total de caracteres, hasta donde llega el codigo fuente
		   int numCaracteres = codigo.length();
		   //Empezamos....
		   while(indice < numCaracteres && !finToken){
			   char c = codigo.charAt(indice);
			   //Aqui hacemos la comprobaciones/Reconocimiento
			   switch(c){
			      case ' ': indice++;//Ignoramos los espacios en blanco;		    	  
			      break;
			      
			      case '\n': {
			    	  numLinea++;
			    	  indice++; //Aumentamos el numero de lineas
			      }
			      break;
			      
			      case '\r': indice++;//Ingnoramos los retornos de carro;
			      break;
			      
			      case '\t': indice++;//Ignoramos los tabuladores;
			      break;
			      
			      case '(': {
			    	  indice++;
			    	  return new Token("(");
			      }
			      
			      case ')': {
			    	  indice++;
			    	  return new Token(")");
			      }
			      
			      case '[': {
			    	  indice++;
			    	  return new Token("[");
			      }
			      
			      case ']': {
			    	  indice++;
			    	  return new Token("]");
			      }
			      
			      case '+': {
			    	  indice++;
			    	  return new Token("+");
			      }
			      
			      case '-': {
			    	  indice++;
			    	  return new Token("-");
			      }
			      
			      case '*': {
			    	  indice++;
			    	  return new Token("*");
			      }
			      
			      case ':' :{
			    	  indice++;
			    	  return new Token(":");
			      }
			      case '/': {
			    	  indice++;
			    	  if(codigo.charAt(indice) == '/')
			    		  while(codigo.charAt(indice) != '\n'){
			    			  //ignoramos los comentarios de una linea tras la doble barra
			    			  indice++;
			    		  }
			    	  else
			    		  return new Token("/");
			      }
			      break;
			      case '%': {
			    	  indice++;
			    	  return new Token("%");
			      }
			      
			      case '<': return new Token(EMenor());
			      
			      case '>': return new Token(EMayor());
			      
			      case '&': return new Token(EAnd());
			      
			      case '|': return new Token(EOr());
			      
			      case ';': {
			    	  indice++;
			    	  return new Token(";");
			      }
			      
			      case ',': {
			    	  indice++;
			    	  return new Token(",");
			      }
			      
			      case '@': {
			    	  indice++;
			    	  return new Token("@");
			      }
			      case '.': {
			    	  indice++;
			    	  return new Token(".");
			      }
			      
			      case '=': return new Token(EIgual());
			      
			      default:{
			    	  //Reconoce las palabras es decir los identificadores
			    	  if(esPalabra(c)){
			    		  return EIdentificador(c);
			    	  }
			    	  //Reconoce los numeros, (del tipo que sean)
			    	  else if(esNumero(c)){
			    		  return ENumeros(c);
			    	  }
			    	  else{
			    		errores.addError("Secuencia no reconocida por el lenguaje - Linea "+numLinea);
			    		throw new Exception("Secuencia no reconocida por el lenguaje - Linea "+numLinea);
			    	  }
			    		  
			      }
			   }
		   }
		
		   return null;
    }
	   public int getNumLinea(){
		   return numLinea;
	   }
private String EIgual() {
	char c;
	indice++;
    c = codigo.charAt(indice);
    if(c == '='){
    	indice++;
    	return ("==");
    }
	else		 
	   return ("=");
	}

private String EOr() {
	char c;
	indice++;
    c = codigo.charAt(indice);
    if(c == '|'){
    	indice++;
    	return ("||");
    }
    else		 
	   return ("|");
	}

private String EAnd() {
	char c;
	indice++;
    c = codigo.charAt(indice);
    if(c == '&'){
    	indice++;
	   return ("&&");
    }
	else		 
	   return ("&");
	}

private String EMayor() {
	char c;
	indice++;
    c = codigo.charAt(indice);
    if(c == '='){
    	indice++;
    	return (">=");
    }else		 
	   return (">");
	}

private String EMenor() {
	char c;
	indice++;
    c = codigo.charAt(indice);
    if(c == '='){
    	indice++;
    	return ("<=");
    }
    else if(c == '>'){
    	indice++;
    	return ("<>");
    }else		 
	   return ("<");
	}

//Metodo privado en el cual compruebo que el caracter puede ser un numero
private boolean esNumero(char c) {
	return (Character.getNumericValue(c) >= 0) && (Character.getNumericValue(c)< 10);
}
//Metodo privado en el cual compruebo que el caracter puede ser una letra
private boolean esPalabra(char c) {
	return (Character.getNumericValue(c)> 9) && (Character.getNumericValue(c)< 36);
}
/**
 * Se encarga de reconocer el token:
 *    -TIdentificador
 */
private Token EIdentificador(char c){
	  StringBuffer palabra=new StringBuffer();
	  while(esPalabra(c) || esNumero(c)){
		  palabra.append(c);
		  indice++;
		  c = codigo.charAt(indice);
	  }
	  Token ident = new Token(palabra.toString());
	  if(palResvTipo .esReservada(palabra.toString()))
		  ident.setSubclase(0);
	  else if(palFunc.esReservada(palabra.toString()))
		  ident.setSubclase(1);
	  else
		  ident.setIdentificador(256);
	  return ident;
  }

/**
 * Se encarga de reconocer el token:
 *    -TEnteros
 *    -TDoubles
 * @throws Exception 
 */
private Token ENumeros(char c) throws Exception {	
		  StringBuffer numero=new StringBuffer();
		  while(esNumero(c)){
			  numero.append(c);
			  indice++;
			  c = codigo.charAt(indice);
		  }
		  //NO se acepta: 12dde, se sale del programa y da un error.
		  if(esPalabra(c)){
			  errores.addError("Error de lexico[Linea "+ numLinea +"] : " + numero);
			  //System.exit(-1);
		  }
		  //Pasamos a reconocer los reales!!!(en el caso que lo sea)
		  else if(c == '.'){
			  numero.append(c);
			  indice++;
			  c = codigo.charAt(indice);
			  while(esNumero(c)){
				  numero.append(c);
				  indice++;
				  c = codigo.charAt(indice);
			  }
			  //NO se acepta: 12.5dde, se sale del programa y da un error.
			  if(esPalabra(c)){
				  errores.addError("Error de lexico[Linea "+ numLinea +"] : " + numero);
				  //-throw new Exception();
				  //System.exit(-1);
			  }
			  Token aux = new Token(numero.toString());
			  aux.setIdentificador(258);
			  return aux; 
		  }
		  else{
			  Token aux = new Token(numero.toString());
			  aux.setIdentificador(257);
			  return aux;
		  }
		return null;
}
}
